; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -passes=instcombine -S -o - %s | FileCheck %s

; Check that we don't replace `atomicrmw <op> LHS, 0` with `load atomic LHS`.
; Doing that would lose the store semantic of the `atomicrmw` operation.
; This may enable some other optimizations that would otherwise be illegal when
; the store semantic was present (e.g., like dropping a fence).

; Idempotent atomicrmw are still canonicalized.
define i32 @atomic_add_zero(ptr %addr) {
; CHECK-LABEL: @atomic_add_zero(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i32 0 monotonic, align 4
; CHECK-NEXT:    ret i32 [[RES]]
;
  %res = atomicrmw add ptr %addr, i32 0 monotonic
  ret i32 %res
}

define i32 @atomic_or_zero(ptr %addr) {
; CHECK-LABEL: @atomic_or_zero(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i32 0 monotonic, align 4
; CHECK-NEXT:    ret i32 [[RES]]
;
  %res = atomicrmw add ptr %addr, i32 0 monotonic
  ret i32 %res
}

; Idempotent atomicrmw are still canonicalized.
define i32 @atomic_sub_zero(ptr %addr) {
; CHECK-LABEL: @atomic_sub_zero(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i32 0 monotonic, align 4
; CHECK-NEXT:    ret i32 [[RES]]
;
  %res = atomicrmw sub ptr %addr, i32 0 monotonic
  ret i32 %res
}

; Idempotent atomicrmw are still canonicalized.
define i32 @atomic_and_allones(ptr %addr) {
; CHECK-LABEL: @atomic_and_allones(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i32 0 monotonic, align 4
; CHECK-NEXT:    ret i32 [[RES]]
;
  %res = atomicrmw and ptr %addr, i32 -1 monotonic
  ret i32 %res
}

; Idempotent atomicrmw are still canonicalized.
define i32 @atomic_umin_uint_max(ptr %addr) {
; CHECK-LABEL: @atomic_umin_uint_max(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i32 0 monotonic, align 4
; CHECK-NEXT:    ret i32 [[RES]]
;
  %res = atomicrmw umin ptr %addr, i32 -1 monotonic
  ret i32 %res
}

; Idempotent atomicrmw are still canonicalized.
define i32 @atomic_umax_zero(ptr %addr) {
; CHECK-LABEL: @atomic_umax_zero(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i32 0 monotonic, align 4
; CHECK-NEXT:    ret i32 [[RES]]
;
  %res = atomicrmw umax ptr %addr, i32 0 monotonic
  ret i32 %res
}

; Idempotent atomicrmw are still canonicalized.
define i8 @atomic_min_smax_char(ptr %addr) {
; CHECK-LABEL: @atomic_min_smax_char(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i8 0 monotonic, align 1
; CHECK-NEXT:    ret i8 [[RES]]
;
  %res = atomicrmw min ptr %addr, i8 127 monotonic
  ret i8 %res
}

; Idempotent atomicrmw are still canonicalized.
define i8 @atomic_max_smin_char(ptr %addr) {
; CHECK-LABEL: @atomic_max_smin_char(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i8 0 monotonic, align 1
; CHECK-NEXT:    ret i8 [[RES]]
;
  %res = atomicrmw max ptr %addr, i8 -128 monotonic
  ret i8 %res
}

; Idempotent atomicrmw are still canonicalized.
define float @atomic_fsub_zero(ptr %addr) {
; CHECK-LABEL: @atomic_fsub_zero(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw fadd ptr [[ADDR:%.*]], float -0.000000e+00 monotonic, align 4
; CHECK-NEXT:    ret float [[RES]]
;
  %res = atomicrmw fsub ptr %addr, float 0.0 monotonic
  ret float %res
}

define float @atomic_fadd_zero(ptr %addr) {
; CHECK-LABEL: @atomic_fadd_zero(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw fadd ptr [[ADDR:%.*]], float -0.000000e+00 monotonic, align 4
; CHECK-NEXT:    ret float [[RES]]
;
  %res = atomicrmw fadd ptr %addr, float -0.0 monotonic
  ret float %res
}

; Idempotent atomicrmw are still canonicalized.
define float @atomic_fsub_canon(ptr %addr) {
; CHECK-LABEL: @atomic_fsub_canon(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw fadd ptr [[ADDR:%.*]], float -0.000000e+00 release, align 4
; CHECK-NEXT:    ret float [[RES]]
;
  %res = atomicrmw fsub ptr %addr, float 0.0 release
  ret float %res
}

define float @atomic_fadd_canon(ptr %addr) {
; CHECK-LABEL: @atomic_fadd_canon(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw fadd ptr [[ADDR:%.*]], float -0.000000e+00 release, align 4
; CHECK-NEXT:    ret float [[RES]]
;
  %res = atomicrmw fadd ptr %addr, float -0.0 release
  ret float %res
}

; Can't replace a volatile w/a load; this would eliminate a volatile store.
define i64 @atomic_sub_zero_volatile(ptr %addr) {
; CHECK-LABEL: @atomic_sub_zero_volatile(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw volatile sub ptr [[ADDR:%.*]], i64 0 acquire, align 8
; CHECK-NEXT:    ret i64 [[RES]]
;
  %res = atomicrmw volatile sub ptr %addr, i64 0 acquire
  ret i64 %res
}


; Check that the transformation properly preserve the syncscope.
; Idempotent atomicrmw are still canonicalized.
define i16 @atomic_syncscope(ptr %addr) {
; CHECK-LABEL: @atomic_syncscope(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i16 0 syncscope("some_syncscope") acquire, align 2
; CHECK-NEXT:    ret i16 [[RES]]
;
  %res = atomicrmw or ptr %addr, i16 0 syncscope("some_syncscope") acquire
  ret i16 %res
}

; By eliminating the store part of the atomicrmw, we would get rid of the
; release semantic, which is incorrect.  We can canonicalize the operation.
define i16 @atomic_seq_cst(ptr %addr) {
; CHECK-LABEL: @atomic_seq_cst(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i16 0 seq_cst, align 2
; CHECK-NEXT:    ret i16 [[RES]]
;
  %res = atomicrmw add ptr %addr, i16 0 seq_cst
  ret i16 %res
}

; Check that the transformation does not apply when the value is changed by
; the atomic operation (non zero constant).
define i16 @atomic_add_non_zero(ptr %addr) {
; CHECK-LABEL: @atomic_add_non_zero(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw add ptr [[ADDR:%.*]], i16 2 monotonic, align 2
; CHECK-NEXT:    ret i16 [[RES]]
;
  %res = atomicrmw add ptr %addr, i16 2 monotonic
  ret i16 %res
}

; Idempotent atomicrmw are still canonicalized.
define i16 @atomic_xor_zero(ptr %addr) {
; CHECK-LABEL: @atomic_xor_zero(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i16 0 monotonic, align 2
; CHECK-NEXT:    ret i16 [[RES]]
;
  %res = atomicrmw xor ptr %addr, i16 0 monotonic
  ret i16 %res
}

; Check that the transformation does not apply when the ordering is
; incompatible with a load (release).  Do canonicalize.
define i16 @atomic_release(ptr %addr) {
; CHECK-LABEL: @atomic_release(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i16 0 release, align 2
; CHECK-NEXT:    ret i16 [[RES]]
;
  %res = atomicrmw sub ptr %addr, i16 0 release
  ret i16 %res
}

; Check that the transformation does not apply when the ordering is
; incompatible with a load (acquire, release).  Do canonicalize.
define i16 @atomic_acq_rel(ptr %addr) {
; CHECK-LABEL: @atomic_acq_rel(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i16 0 acq_rel, align 2
; CHECK-NEXT:    ret i16 [[RES]]
;
  %res = atomicrmw xor ptr %addr, i16 0 acq_rel
  ret i16 %res
}

define i32 @sat_or_allones(ptr %addr) {
; CHECK-LABEL: @sat_or_allones(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 -1 monotonic, align 4
; CHECK-NEXT:    ret i32 [[RES]]
;
  %res = atomicrmw or ptr %addr, i32 -1 monotonic
  ret i32 %res
}

define i32 @sat_and_zero(ptr %addr) {
; CHECK-LABEL: @sat_and_zero(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 0 monotonic, align 4
; CHECK-NEXT:    ret i32 [[RES]]
;
  %res = atomicrmw and ptr %addr, i32 0 monotonic
  ret i32 %res
}

define i32 @sat_umin_uint_min(ptr %addr) {
; CHECK-LABEL: @sat_umin_uint_min(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 0 monotonic, align 4
; CHECK-NEXT:    ret i32 [[RES]]
;
  %res = atomicrmw umin ptr %addr, i32 0 monotonic
  ret i32 %res
}

define i32 @sat_umax_uint_max(ptr %addr) {
; CHECK-LABEL: @sat_umax_uint_max(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 -1 monotonic, align 4
; CHECK-NEXT:    ret i32 [[RES]]
;
  %res = atomicrmw umax ptr %addr, i32 -1 monotonic
  ret i32 %res
}

define i8 @sat_min_smin_char(ptr %addr) {
; CHECK-LABEL: @sat_min_smin_char(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i8 -128 monotonic, align 1
; CHECK-NEXT:    ret i8 [[RES]]
;
  %res = atomicrmw min ptr %addr, i8 -128 monotonic
  ret i8 %res
}

define i8 @sat_max_smax_char(ptr %addr) {
; CHECK-LABEL: @sat_max_smax_char(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i8 127 monotonic, align 1
; CHECK-NEXT:    ret i8 [[RES]]
;
  %res = atomicrmw max ptr %addr, i8 127 monotonic
  ret i8 %res
}

define double @sat_fadd_nan(ptr %addr) {
; CHECK-LABEL: @sat_fadd_nan(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], double 0x7FF00000FFFFFFFF release, align 8
; CHECK-NEXT:    ret double [[RES]]
;
  %res = atomicrmw fadd ptr %addr, double 0x7FF00000FFFFFFFF release
  ret double %res
}

define double @sat_fsub_nan(ptr %addr) {
; CHECK-LABEL: @sat_fsub_nan(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], double 0x7FF00000FFFFFFFF release, align 8
; CHECK-NEXT:    ret double [[RES]]
;
  %res = atomicrmw fsub ptr %addr, double 0x7FF00000FFFFFFFF release
  ret double %res
}

define void @sat_fsub_nan_unused(ptr %addr) {
; CHECK-LABEL: @sat_fsub_nan_unused(
; CHECK-NEXT:    [[TMP1:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], double 0x7FF00000FFFFFFFF monotonic, align 8
; CHECK-NEXT:    ret void
;
  atomicrmw fsub ptr %addr, double 0x7FF00000FFFFFFFF monotonic
  ret void
}

define void @xchg_unused_monotonic(ptr %addr) {
; CHECK-LABEL: @xchg_unused_monotonic(
; CHECK-NEXT:    [[TMP1:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 0 monotonic, align 4
; CHECK-NEXT:    ret void
;
  atomicrmw xchg ptr %addr, i32 0 monotonic
  ret void
}

define void @xchg_unused_release(ptr %addr) {
; CHECK-LABEL: @xchg_unused_release(
; CHECK-NEXT:    [[TMP1:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 -1 release, align 4
; CHECK-NEXT:    ret void
;
  atomicrmw xchg ptr %addr, i32 -1 release
  ret void
}

define void @xchg_unused_under_aligned(ptr %addr) {
; CHECK-LABEL: @xchg_unused_under_aligned(
; CHECK-NEXT:    [[TMP1:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 -1 release, align 1
; CHECK-NEXT:    ret void
;
  atomicrmw xchg ptr %addr, i32 -1 release, align 1
  ret void
}

define void @xchg_unused_over_aligned(ptr %addr) {
; CHECK-LABEL: @xchg_unused_over_aligned(
; CHECK-NEXT:    [[TMP1:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 -1 release, align 8
; CHECK-NEXT:    ret void
;
  atomicrmw xchg ptr %addr, i32 -1 release, align 8
  ret void
}

define void @xchg_unused_seq_cst(ptr %addr) {
; CHECK-LABEL: @xchg_unused_seq_cst(
; CHECK-NEXT:    [[TMP1:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 0 seq_cst, align 4
; CHECK-NEXT:    ret void
;
  atomicrmw xchg ptr %addr, i32 0 seq_cst
  ret void
}

define void @xchg_unused_volatile(ptr %addr) {
; CHECK-LABEL: @xchg_unused_volatile(
; CHECK-NEXT:    [[TMP1:%.*]] = atomicrmw volatile xchg ptr [[ADDR:%.*]], i32 0 monotonic, align 4
; CHECK-NEXT:    ret void
;
  atomicrmw volatile xchg ptr %addr, i32 0 monotonic
  ret void
}

define void @sat_or_allones_unused(ptr %addr) {
; CHECK-LABEL: @sat_or_allones_unused(
; CHECK-NEXT:    [[TMP1:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], i32 -1 monotonic, align 4
; CHECK-NEXT:    ret void
;
  atomicrmw or ptr %addr, i32 -1 monotonic
  ret void
}

define void @undef_operand_unused(ptr %addr) {
; CHECK-LABEL: @undef_operand_unused(
; CHECK-NEXT:    [[TMP1:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i32 undef monotonic, align 4
; CHECK-NEXT:    ret void
;
  atomicrmw or ptr %addr, i32 undef monotonic
  ret void
}

define i32 @undef_operand_used(ptr %addr) {
; CHECK-LABEL: @undef_operand_used(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw or ptr [[ADDR:%.*]], i32 undef monotonic, align 4
; CHECK-NEXT:    ret i32 [[RES]]
;
  %res = atomicrmw or ptr %addr, i32 undef monotonic
  ret i32 %res
}

define double @sat_fmax_inf(ptr %addr) {
; CHECK-LABEL: @sat_fmax_inf(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], double 0x7FF0000000000000 monotonic, align 8
; CHECK-NEXT:    ret double [[RES]]
;
  %res = atomicrmw fmax ptr %addr, double 0x7FF0000000000000 monotonic
  ret double %res
}

define double @no_sat_fmax_inf(ptr %addr) {
; CHECK-LABEL: @no_sat_fmax_inf(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw fmax ptr [[ADDR:%.*]], double 1.000000e-01 monotonic, align 8
; CHECK-NEXT:    ret double [[RES]]
;
  %res = atomicrmw fmax ptr %addr, double 1.000000e-01 monotonic
  ret double %res
}

define double @sat_fmin_inf(ptr %addr) {
; CHECK-LABEL: @sat_fmin_inf(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw xchg ptr [[ADDR:%.*]], double 0xFFF0000000000000 monotonic, align 8
; CHECK-NEXT:    ret double [[RES]]
;
  %res = atomicrmw fmin ptr %addr, double 0xFFF0000000000000 monotonic
  ret double %res
}

define double @no_sat_fmin_inf(ptr %addr) {
; CHECK-LABEL: @no_sat_fmin_inf(
; CHECK-NEXT:    [[RES:%.*]] = atomicrmw fmin ptr [[ADDR:%.*]], double 1.000000e-01 monotonic, align 8
; CHECK-NEXT:    ret double [[RES]]
;
  %res = atomicrmw fmin ptr %addr, double 1.000000e-01 monotonic
  ret double %res
}
